﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rosetta.Composition;
using Rosetta.Default;
using Rosetta.Default.AddIns.Text;

namespace Rosetta.Tests
{
    /// <summary>
    /// Summary description for TemplateTests
    /// </summary>
    [TestClass]
    public class TemplateTests
    {
        #region Public Methods

        [TestMethod]
        public void TestEmpty()
        {
            var container = new Container();
            var template = new Template(container);

            Assert.IsTrue(TemplateValidator.IsComplete(template));
            Assert.AreEqual(0, TemplateDepthCalculator.GetDepth(template));

            SimpleExecutor.Run(template);
        }

        [TestMethod]
        public void TestMissingArgument()
        {
            var container = new Container();
            container.LoadAssembly(typeof(StringInputter).Assembly);
            container.LoadAssembly(typeof(TemplateTests).Assembly);

            var template = new Template(container);
            var startNode = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetString");
            var convertNode = startNode.AddChild("Rosetta.Default.AddIns.TypeConverter, Rosetta.Default", "StringToInt32", 0).EndPoint;
            var rangeConverter = convertNode.AddChild("Rosetta.Default.AddIns.RangeConverter, Rosetta.Default", "GetInt32Range", 0).EndPoint;
            var convert2Node = rangeConverter.AddChild("Rosetta.Default.AddIns.TypeConverter, Rosetta.Default", "Int32ToString", 0).EndPoint;
            convert2Node.AddChild("Rosetta.Tests.TemplateTests+MockOutputter, Rosetta.Tests", "Output", 0);

            Assert.IsFalse(TemplateValidator.IsComplete(template));
            Assert.AreEqual(1, rangeConverter.MissingArguments.Length);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestDoubleArgument()
        {
            var container = new Container();
            container.LoadAssembly(typeof(StringInputter).Assembly);
            container.LoadAssembly(typeof(TemplateTests).Assembly);

            var template = new Template(container);
            var startNode = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetString");
            var start2Node = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetString");
            var convertNode = startNode.AddChild("Rosetta.Default.AddIns.TypeConverter, Rosetta.Default", "StringToInt32", 0).EndPoint;
            start2Node.AddChild(convertNode, 0);
        }

        [TestMethod]
        public void TestStartPoints()
        {
            var container = new Container();
            container.LoadAssembly(typeof(StringInputter).Assembly);
            container.LoadAssembly(typeof(TemplateTests).Assembly);

            var template = new Template(container);
            var startNode = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetString");
            Assert.IsNotNull(startNode);
            startNode.Context = "Test";
            Assert.AreEqual("Test", startNode.Context);

            Assert.IsFalse(TemplateValidator.IsComplete(template));
            Assert.AreEqual(1, TemplateDepthCalculator.GetDepth(template));
        }

        [TestMethod]
        public void TestCompleted()
        {
            var container = new Container();
            container.LoadAssembly(typeof(StringInputter).Assembly);
            container.LoadAssembly(typeof(TemplateTests).Assembly);

            var count = container.GetAddInsByArgumentType(typeof(string)).Count();
            Assert.IsTrue(count > 0);

            var template = new Template(container);

            var startNode = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetString");
            Assert.IsNotNull(startNode);
            startNode.Context = "Test";
            Assert.AreEqual("Test", startNode.Context);

            var startNode2 = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetInt32");
            startNode2.Context = "666";

            // 3 output nodes
            startNode.AddChild("Rosetta.Tests.TemplateTests+MockOutputter, Rosetta.Tests", "Output", 0).HasMultipleResults = false;
            startNode.AddChild("Rosetta.Tests.TemplateTests+MockOutputter, Rosetta.Tests", "Output", 0).HasMultipleResults = false;
            var outputNode = startNode2.AddChild("Rosetta.Tests.TemplateTests+MockOutputter, Rosetta.Tests", "Output", 0).EndPoint;

            Assert.IsTrue(TemplateValidator.IsComplete(template));
            Assert.AreEqual(2, TemplateDepthCalculator.GetDepth(template));

            MockOutputter.Results.Clear();

            SimpleExecutor.Run(template);

            Assert.AreEqual(3, MockOutputter.Results.Count);
            Assert.IsTrue(MockOutputter.Results.Contains("Test"));
            Assert.IsTrue(MockOutputter.Results.Contains(666));

            Assert.AreEqual(1, TemplateDepthCalculator.GetDepth(startNode));
            Assert.AreEqual(2, TemplateDepthCalculator.GetDepth(outputNode));
        }

        [TestMethod]
        public void TestConverters()
        {
            var container = new Container();
            container.LoadAssembly(typeof(StringInputter).Assembly);
            container.LoadAssembly(typeof(TemplateTests).Assembly);

            var template = new Template(container);
            var startNode = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetString");
            startNode.Context = "666";

            var typeConvertNode = startNode.AddChild("Rosetta.Default.AddIns.TypeConverter, Rosetta.Default", "StringToInt32", 0).EndPoint;
            var outputNode = typeConvertNode.AddChild("Rosetta.Tests.TemplateTests+MockOutputter, Rosetta.Tests", "Output", 0).EndPoint;

            MockOutputter.Results.Clear();

            SimpleExecutor.Run(template);

            Assert.IsTrue(MockOutputter.Results.Contains(666));

            Assert.AreEqual(3, TemplateDepthCalculator.GetDepth(outputNode));
        }

        [TestMethod]
        public void TestRangeOutputter()
        {
            var container = new Container();
            container.LoadAssembly(typeof(StringInputter).Assembly);
            container.LoadAssembly(typeof(TemplateTests).Assembly);

            var template = new Template(container);
            var startNode = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetInt32");
            startNode.Context = "1";
            var countNode = template.AddInputNode("Rosetta.Default.AddIns.Text.StringInputter, Rosetta.Default", "GetInt32");
            countNode.Context = "10";

            var rangeConverterNode = startNode.AddChild("Rosetta.Default.AddIns.RangeConverter, Rosetta.Default", "GetInt32Range", 0).EndPoint;
            countNode.AddChild(rangeConverterNode, 1);

            var outputNode = rangeConverterNode.AddChild("Rosetta.Tests.TemplateTests+MockOutputter, Rosetta.Tests", "Output", 0).EndPoint;

            MockOutputter.Results.Clear();

            SimpleExecutor.Run(template);

            Assert.AreEqual(10, MockOutputter.Results.Count);
            Assert.IsTrue(MockOutputter.Results.Contains(5));
            Assert.AreEqual(3, TemplateDepthCalculator.GetDepth(outputNode));
        }

        #endregion

        #region Nested Types

        private sealed class MockOutputter : Outputter
        {
            #region Fields

            public static readonly List<object> Results = new List<object>();

            #endregion

            #region Constructors

            public MockOutputter()
                : base("MockOutputter")
            {
                Add<object>("Output", Results.Add);
            }

            #endregion
        }

        #endregion
    }
}